#include <panel.h>
#include <fstream>
#include "actor.h"
#include "level.h"
#include "queue.h"
#include "extras.h"
#include "game.h"
#include "buffer.h"
using std::endl;

extern Actor Player,LoadedActor[NUM_ACTORS];
extern AttackInfo LoadedAttack[NUM_ATTACKS];
extern Item LoadedItem[NUM_ITEMS];
extern Level Map;
extern Queue Q;
extern WINDOW* mapwin;
extern WINDOW* msgwin;
extern WINDOW* statwin;
extern PANEL* mappan;
extern PANEL* msgpan;
extern PANEL* statpan;
extern GameInfo Game;
extern Buffer Buff;

Actor::Actor() : gold(0),allied(false),AI(STANDARD),maxhp(1),curhp(1),maxmp(5),curmp(5),speed(100),size(1),weapon(NULL),talisman(NULL),player_seen(NULL),target(NULL){
	//
}
Actor::Actor(Actor& in,int r,int c) : Object(in.name,in.a_name,in.the_name,in.symbol,in.color,r,c,in.id),gold(0),allied(false),AI(in.AI),maxhp(in.maxhp),curhp(in.maxhp),maxmp(in.maxmp),curmp(in.maxmp),speed(in.speed),size(in.size),weapon(NULL),talisman(NULL),attack(in.attack),player_seen(NULL),target(NULL),has_attr(in.has_attr){
	//
}
string Actor::AName(){
	if(name == "you"){
		return name;
	}
	if(nickname != ""){
		return nickname;
	}
	if(allied){
		return "your " + name;
	}
	return a_name;
}
string Actor::TheName(){
	if(name == "you"){
		return name;
	}
	if(nickname != ""){
		return nickname;
	}
	if(allied){
		return "your " + name;
	}
	return the_name;
}
void Actor::CloneSelf(int r,int c){
	if(Map.ActorPtr(r,c)==NULL){
		Actor* temp = new Actor(*this,r,c);
		Map.SetActorPtr(temp,r,c);
		temp->QS();
		if(AI==NECRO){
			if(Roll(1,2)==2){
				temp->AddAttack(8);
			}
			else{
				temp->AddAttack(9);
			}
			//
			vector<int> p;
			for(int i=2;i<=4;++i){
				p.insert(p.end(),i);
			}
			for(int i=5;i<=8;++i){
				if(Roll(1,2)==2 || int(Game.CurrentLevel())>=int(FOREST_2)){
					p.insert(p.end(),i);
				}
			}
			for(int i=9;i<=12;++i){
				if(Roll(1,4)==4 || int(Game.CurrentLevel())>=int(CAVE_2)){
					p.insert(p.end(),i);
				}
			}
			int number_of_minions = rand()%2 + int(Game.CurrentLevel());
			if(number_of_minions > 4){ number_of_minions = 4; }
			for(int i=0;i<number_of_minions;++i){
				temp->AddMinion(new Actor(LoadedActor[p[rand()%p.size()]],-1,-1));
			}
		}
	}
}
void Actor::Define(int id_,string name_,char symbol_,int color_,AIType AI_,int maxhp_,int speed_){ //size and mp?
	id=id_;
	name=name_;
	if(name=="you"){
		the_name = name;
		a_name = name;
	}
	else{
		the_name="the " + name;
		char* sub = (char *)name.c_str();
		switch(sub[0]){
			case 'a':
			case 'e':
			case 'i':
			case 'o':
			case 'u':
			case 'A':
			case 'E':
			case 'I':
			case 'O':
			case 'U':
				a_name = "an " + name;
				break;
			default:
				a_name = "a " + name;
				break;
		}
	}
	symbol=symbol_;
	color=color_;
	AI=AI_;
	maxhp=maxhp_;
	curhp=maxhp;
	speed=speed_;
}
void Actor::RFG(bool check_torch){
	int size = inv.size();
	if(size > 0){
		for(int i=0;i<size;++i){
			delete inv[i];
		}
	}
	Q.KillEvents(this,ANY_EVENT);
	Map.RemoveTargets(this);
	Map.DelActorPtr(row,col);
	delete this;
}
/*void Actor::Save(ofstream& out){
	out << int(AI) << endl << maxhp << endl << curhp << endl << speed << endl << xp << endl << level << endl << light_radius << endl;
	out << '(' << endl;
	for(int i=0;i<inv.size();++i){
		out << inv[i] << endl;
	}
	out << ')' << endl;
	for(int i=0;i<int(NUM_ATTRS);++i){
		out << has_attr[AttrType(i)] << endl;
	}
	for(int i=0;i<int(NUM_SKILLS);++i){
		out << has_skill[SkillType(i)] << endl;
	}
	for(int i=0;i<int(NUM_SKILLS);++i){
		out << feat_points[SkillType(i)] << endl;
	}
	for(int i=0;i<int(NUM_FEATS);++i){
		out << has_feat[FeatType(i)] << endl;
	}
	for(int i=0;i<int(NUM_SPELLS);++i){
		out << has_spell[SpellType(i)] << endl;
	}
	out << int(weapon) << endl << int(armor) << endl;
	for(int i=0;i<int(NUM_UPGRADES);++i){
		out << has_upgrade[UpgradeType(i)] << endl;
	}
	for(int i=0;i<9;++i){
		out << int(F[i]) << endl;
	}
	out << '(' << endl;
	for(int i=0;i<inv.size();++i){
		out << attack[i] << endl;
	}
	out << ')' << endl;
	out << recover_time << endl << regen_time << endl;
	for(int i=0;i<ROWS;++i){
		for(int j=0;j<COLS;++j){
			out << explored[i][j] << endl;
		}
	}
	out << player_seen << endl;
}*/
void Actor::Move(int r,int c,bool swap){
	if(r>=0 && r<ROWS && c>=0 && c<COLS){
		if(Map.ActorPtr(r,c)==NULL){
			Map.SetActorPtr(this,r,c);
			if(row>=0 && row<ROWS && col>=0 && col<COLS){
				Map.DelActorPtr(row,col);
			}
			row=r;
			col=c;
		}
		else{
			if(swap){
				Map.DelActorPtr(row,col);
				Map.ActorPtr(r,c)->Move(row,col);
				Map.SetActorPtr(this,r,c);
				row=r;
				col=c;
			}
		}
	}
}
bool Actor::HasLOS(int r,int c){
	int y2 = r;
	int x2 = c;
	int y1 = row;	// bresenham (inverted y)
	int x1 = col;
	int dx = abs(x2-x1);
	int dy = abs(y2-y1);
	int er=0;
	if(dx <= 1 && dy <= 1){
		return true;
	}
	if(y1==y2){
		if(x1==x2){	//Same position
			return 1;
		}
		for(;x1<x2;++x1){	//Drawing right
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return 0;
			}
		}
		for(;x1>x2;--x1){	//Drawing left
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return 0;
			}
		}
		return 1;
	}
	if(x1==x2){
		for(;y1>y2;--y1){	//Drawing up
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return 0;
			}
		}
		for(;y1<y2;++y1){	//Drawing down
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return 0;
			}
		}
		return 1;
	}
	if(y1+x1==y2+x2){	//Slope equals -1
		for(;x1<x2;++x1){	//Drawing up and right
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return 0;
			}
			--y1;
		}
		for(;x1>x2;--x1){	//Drawing down and left
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return 0;
			}
			++y1;
		}
		return 1;
	}
	if(y1-x1==y2-x2){	//Slope equals 1
		for(;x1<x2;++x1){	//Drawing down and right
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return 0;
			}
			++y1;
		}
		for(;x1>x2;--x1){	//Drawing up and left
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return 0;
			}
			--y1;
		}
		return 1;
	}
	if(y1<y2){	//Drawing down
		if(x1<x2){	//Drawing right
			if(dx>dy){	//With a slope less than 1
				for(;x1<x2;++x1){
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return 0;
					}
					er=er+dy;
					if((er<<1)>=dx){
						++y1;
						er=er-dx;
					}
				}
				return 1;
			}
			else{	//slope is more than 1
				for(;y1<y2;++y1){
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return 0;
					}
					er=er+dx;
					if((er<<1)>=dy){
						++x1;
						er=er-dy;
					}
				}
				return 1;
			}
		}
		else{	//Drawing left
			if(dx>dy){	//With a slope less than 1
				for(;x1>x2;--x1){
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return 0;
					}
					er=er+dy;
					if((er<<1)>=dx){
						++y1;
						er=er-dx;
					}
				}
				return 1;
			}
			else{	//slope is more than 1
				for(;y1<y2;++y1){
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return 0;
					}
					er=er+dx;
					if((er<<1)>=dy){
						--x1;
						er=er-dy;
					}
				}
				return 1;
			}
		}
	}
	else{	//Drawing up
		if(x1<x2){	//Drawing right
			if(dx>dy){	//With a slope less than 1
				for(;x1<x2;++x1){
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return 0;
					}
					er=er+dy;
					if((er<<1)>=dx){
						--y1;
						er=er-dx;
					}
				}
				return 1;
			}
			else{	//slope is more than 1
				for(;y1>y2;--y1){
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return 0;
					}
					er=er+dx;
					if((er<<1)>=dy){
						++x1;
						er=er-dy;
					}
				}
				return 1;
			}
		}
		else{	//Drawing left
			if(dx>dy){	//With a slope less than 1
				for(;x1>x2;--x1){
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return 0;
					}
					er=er+dy;
					if((er<<1)>=dx){
						--y1;
						er=er-dx;
					}
				}
				return 1;
			}
			else{	//slope is more than 1
				for(;y1>y2;--y1){
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return 0;
					}
					er=er+dx;
					if((er<<1)>=dy){
						--x1;
						er=er-dy;
					}
				}
				return 1;
			}
		}
	}
}
bool Actor::CanSee(Object* ptr){
	int r = ptr->Row();
	int c = ptr->Col();
	if(Map.TilePtr(r,c)->IsOpaque()){
		return CanSeeOpaque(Map.TilePtr(r,c));
	}
	else{
		if(!ptr->IsHidden()){
			if(abs(r-row)<=1 && abs(c-col)<=1){ //this part isn't really needed, but it doesn't hurt either
				return true;
			}
			else{
				if(HasLOS(r,c)){
					if(abs(r-row)<=3 && abs(c-col)<=3){
						return true;
					}
					else{
						if(Map.TilePtr(r,c)->IsLit() || HasAttr(DARKVISION) || Game.CurrentLevel()==TOWN){
							return true;		//or just everything, if you have darkvision
						}
						else{
							if(HasAttr(LOW_LIGHT_VISION) && DistanceFrom(r,c)<=MAX_RADIUS){
								return true; //low-light vision is good to 12 spaces
							}
							else{
								if(AI!=HUMAN && DistanceFrom(r,c)<=MAX_RADIUS){
									return true; //implied low-light vision
								}
							}
						}
					}
				}
			}
		}
	}
	return false;
}
bool Actor::CanSee(int r,int c){
	return CanSee(Map.TilePtr(r,c));
}
bool Actor::CanSeeOpaque(Object* ptr){
	int y1 = row;
	int x1 = col;
	int y2 = ptr->Row();
	int x2 = ptr->Col();
	int dx = abs(x2-x1);
	int dy = abs(y2-y1);
	if(dx <= 1 && dy <= 1){
		return true;
	}
	if(y1 == y2){
		if(x1 > x2){ //4
			if(!Map.TilePtr(y2,x2+1)->IsOpaque() && CanSee(y2,x2+1)){
				return true;
			}
		}
		else{ //6
			if(!Map.TilePtr(y2,x2-1)->IsOpaque() && CanSee(y2,x2-1)){
				return true;
			}
		}
		return false;
	}
	if(x1 == x2){
		if(y1 > y2){ //8
			if(!Map.TilePtr(y2+1,x2)->IsOpaque() && CanSee(y2+1,x2)){
				return true;
			}
		}
		else{ //2
			if(!Map.TilePtr(y2-1,x2)->IsOpaque() && CanSee(y2-1,x2)){
				return true;
			}
		}
		return false;
	}
	if(y1 + x1 == y2 + x2){
		if(y1 > y2){ //9
			if(!Map.TilePtr(y2+1,x2-1)->IsOpaque() && CanSee(y2+1,x2-1)){
				return true;
			}
		}
		else{ //1
			if(!Map.TilePtr(y2-1,x2+1)->IsOpaque() && CanSee(y2-1,x2+1)){
				return true;
			}
		}
		return false;
	}
	if(y1 - x1 == y2 - x2){
		if(y1 > y2){ //7
			if(!Map.TilePtr(y2+1,x2+1)->IsOpaque() && CanSee(y2+1,x2+1)){
				return true;
			}
		}
		else{ //3
			if(!Map.TilePtr(y2-1,x2-1)->IsOpaque() && CanSee(y2-1,x2-1)){
				return true;
			}
		}
		return false;
	}
	if(y1 > y2){
		if(x1 > x2){
			if(dx > dy){ //between 4 and 7
				if(!Map.TilePtr(y2,x2+1)->IsOpaque() && CanSee(y2,x2+1)){
					return true;
				}
				if(!Map.TilePtr(y2+1,x2+1)->IsOpaque() && CanSee(y2+1,x2+1)){
					return true;
				}
			}
			else{ //between 8 and 7
				if(!Map.TilePtr(y2+1,x2)->IsOpaque() && CanSee(y2+1,x2)){
					return true;
				}
				if(!Map.TilePtr(y2+1,x2+1)->IsOpaque() && CanSee(y2+1,x2+1)){
					return true;
				}
			}
		}
		else{
			if(dx > dy){ //between 6 and 9
				if(!Map.TilePtr(y2,x2-1)->IsOpaque() && CanSee(y2,x2-1)){
					return true;
				}
				if(!Map.TilePtr(y2+1,x2-1)->IsOpaque() && CanSee(y2+1,x2-1)){
					return true;
				}
			}
			else{ //between 8 and 9
				if(!Map.TilePtr(y2+1,x2)->IsOpaque() && CanSee(y2+1,x2)){
					return true;
				}
				if(!Map.TilePtr(y2+1,x2-1)->IsOpaque() && CanSee(y2+1,x2-1)){
					return true;
				}
			}
		}
	}
	else{
		if(x1 > x2){
			if(dx > dy){ //between 4 and 1
				if(!Map.TilePtr(y2,x2+1)->IsOpaque() && CanSee(y2,x2+1)){
					return true;
				}
				if(!Map.TilePtr(y2-1,x2+1)->IsOpaque() && CanSee(y2-1,x2+1)){
					return true;
				}
			}
			else{ //between 2 and 1
				if(!Map.TilePtr(y2-1,x2)->IsOpaque() && CanSee(y2-1,x2)){
					return true;
				}
				if(!Map.TilePtr(y2-1,x2+1)->IsOpaque() && CanSee(y2-1,x2+1)){
					return true;
				}
			}
		}
		else{
			if(dx > dy){ //between 6 and 3
				if(!Map.TilePtr(y2,x2-1)->IsOpaque() && CanSee(y2,x2-1)){
					return true;
				}
				if(!Map.TilePtr(y2-1,x2-1)->IsOpaque() && CanSee(y2-1,x2-1)){
					return true;
				}
			}
			else{ //between 2 and 3
				if(!Map.TilePtr(y2-1,x2)->IsOpaque() && CanSee(y2-1,x2)){
					return true;
				}
				if(!Map.TilePtr(y2-1,x2-1)->IsOpaque() && CanSee(y2-1,x2-1)){
					return true;
				}
			}
		}
	}
	return false;
}
Actor* Actor::LOELine(Object* ptr,int num){
	int i = -1; //this function checks opacity when it should check solidity, i know. fixed in main project branch, i hope.
	int y1 = row;
	int x1 = col;
	int y2 = ptr->Row();
	int x2 = ptr->Col();
	int dx = abs(x2-x1);
	int dy = abs(y2-y1);
	int er=0;
	bool done = false;
	if(y1==y2){
		for(;x1<=x2;++x1){	//Drawing right
			done=true;
			if(Map.ActorPtr(y1,x1)!=NULL){
				++i;
				if(i == num){
					return Map.ActorPtr(y1,x1);
				}
			}
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return NULL;
			}
		}
		if(done){
			return NULL;
		}
		for(;x1>=x2;--x1){	//Drawing left
			if(Map.ActorPtr(y1,x1)!=NULL){
				++i;
				if(i == num){
					return Map.ActorPtr(y1,x1);
				}
			}
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return NULL;
			}
		}
		return NULL;
	}
	if(x1==x2){
		for(;y1>=y2;--y1){	//Drawing up
			done=true;
			if(Map.ActorPtr(y1,x1)!=NULL){
				++i;
				if(i == num){
					return Map.ActorPtr(y1,x1);
				}
			}
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return NULL;
			}
		}
		if(done){
			return NULL;
		}
		for(;y1<=y2;++y1){	//Drawing down
			if(Map.ActorPtr(y1,x1)!=NULL){
				++i;
				if(i == num){
					return Map.ActorPtr(y1,x1);
				}
			}
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return NULL;
			}
		}
		return NULL;
	}
	if(y1+x1==y2+x2){	//Slope equals -1
		for(;x1<=x2;++x1){	//Drawing up and right
			done=true;
			if(Map.ActorPtr(y1,x1)!=NULL){
				++i;
				if(i == num){
					return Map.ActorPtr(y1,x1);
				}
			}
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return NULL;
			}
			--y1;
		}
		if(done){
			return NULL;
		}
		for(;x1>=x2;--x1){	//Drawing down and left
			if(Map.ActorPtr(y1,x1)!=NULL){
				++i;
				if(i == num){
					return Map.ActorPtr(y1,x1);
				}
			}
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return NULL;
			}
			++y1;
		}
		return NULL;
	}
	if(y1-x1==y2-x2){	//Slope equals 1
		for(;x1<=x2;++x1){	//Drawing down and right
			done=true;
			if(Map.ActorPtr(y1,x1)!=NULL){
				++i;
				if(i == num){
					return Map.ActorPtr(y1,x1);
				}
			}
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return NULL;
			}
			++y1;
		}
		if(done){
			return NULL;
		}
		for(;x1>=x2;--x1){	//Drawing up and left
			if(Map.ActorPtr(y1,x1)!=NULL){
				++i;
				if(i == num){
					return Map.ActorPtr(y1,x1);
				}
			}
			if(Map.TilePtr(y1,x1)->IsOpaque()){
				return NULL;
			}
			--y1;
		}
		return NULL;
	}
	if(y1<y2){	//Drawing down
		if(x1<x2){	//Drawing right
			if(dx>dy){	//With a slope less than 1
				for(;x1<=x2;++x1){
					if(Map.ActorPtr(y1,x1)!=NULL){
						++i;
						if(i == num){
							return Map.ActorPtr(y1,x1);
						}
					}
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return NULL;
					}
					er=er+dy;
					if((er<<1)>=dx){
						++y1;
						er=er-dx;
					}
				}
				return NULL;
			}
			else{	//slope is more than 1
				for(;y1<=y2;++y1){
					if(Map.ActorPtr(y1,x1)!=NULL){
						++i;
						if(i == num){
							return Map.ActorPtr(y1,x1);
						}
					}
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return NULL;
					}
					er=er+dx;
					if((er<<1)>=dy){
						++x1;
						er=er-dy;
					}
				}
				return NULL;
			}
		}
		else{	//Drawing left
			if(dx>dy){	//With a slope less than 1
				for(;x1>=x2;--x1){
					if(Map.ActorPtr(y1,x1)!=NULL){
						++i;
						if(i == num){
							return Map.ActorPtr(y1,x1);
						}
					}
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return NULL;
					}
					er=er+dy;
					if((er<<1)>=dx){
						++y1;
						er=er-dx;
					}
				}
				return NULL;
			}
			else{	//slope is more than 1
				for(;y1<=y2;++y1){
					if(Map.ActorPtr(y1,x1)!=NULL){
						++i;
						if(i == num){
							return Map.ActorPtr(y1,x1);
						}
					}
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return NULL;
					}
					er=er+dx;
					if((er<<1)>=dy){
						--x1;
						er=er-dy;
					}
				}
				return NULL;
			}
		}
	}
	else{	//Drawing up
		if(x1<x2){	//Drawing right
			if(dx>dy){	//With a slope less than 1
				for(;x1<=x2;++x1){
					if(Map.ActorPtr(y1,x1)!=NULL){
						++i;
						if(i == num){
							return Map.ActorPtr(y1,x1);
						}
					}
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return NULL;
					}
					er=er+dy;
					if((er<<1)>=dx){
						--y1;
						er=er-dx;
					}
				}
				return NULL;
			}
			else{	//slope is more than 1
				for(;y1>=y2;--y1){
					if(Map.ActorPtr(y1,x1)!=NULL){
						++i;
						if(i == num){
							return Map.ActorPtr(y1,x1);
						}
					}
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return NULL;
					}
					er=er+dx;
					if((er<<1)>=dy){
						++x1;
						er=er-dy;
					}
				}
				return NULL;
			}
		}
		else{	//Drawing left
			if(dx>dy){	//With a slope less than 1
				for(;x1>=x2;--x1){
					if(Map.ActorPtr(y1,x1)!=NULL){
						++i;
						if(i == num){
							return Map.ActorPtr(y1,x1);
						}
					}
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return NULL;
					}
					er=er+dy;
					if((er<<1)>=dx){
						--y1;
						er=er-dx;
					}
				}
				return NULL;
			}
			else{	//slope is more than 1
				for(;y1>=y2;--y1){
					if(Map.ActorPtr(y1,x1)!=NULL){
						++i;
						if(i == num){
							return Map.ActorPtr(y1,x1);
						}
					}
					if(Map.TilePtr(y1,x1)->IsOpaque()){
						return NULL;
					}
					er=er+dx;
					if((er<<1)>=dy){
						--x1;
						er=er-dy;
					}
				}
				return NULL;
			}
		}
	}
}
string Actor::YouAre(){
	string result;
	if(name=="you"){
		result = "you are";
	}
	else{
		result = TheName() + " is";
	}
	return result;
}
string Actor::You(string in,bool ends_in_es){
	string result;
	if(name=="you"){
		result = "you " + in;
	}
	else{
		if(ends_in_es){
			result = TheName() + " " + in + "es";
		}
		else{
			result = TheName() + " " + in + "s";
		}
	}
	return result;
}
void Actor::Input(){
	if(AI==HUMAN){
		InpPC();
	}
	else{
		InpAI();
	}
}
void Actor::InpPC(){
	Map.Draw();
	DisplayStats();
	Cursor();
	Buff.Print();
	Cursor();
	top_panel(mappan);
	update_panels();
	doupdate();
	int inchar = getch();
	switch(inchar){
		case '7':
		case '8':
		case '9':
		case '4':
		case '6':
		case '1':
		case '2':
		case '3':
		case 258:
		case 259:
		case 260:
		case 261:
			{ //now let's hope these work on Windows...
				switch(inchar){
					case 258:
						inchar = '2';
						break;
					case 259:
						inchar = '8';
						break;
					case 260:
						inchar = '4';
						break;
					case 261:
						inchar = '6';
						break;
				}
				int dir = inchar - 48; //ascii 0-9 are 48-57
				if(dir > 0){
					if(TileInDirection(dir)->Col()==0 && (Game.CurrentLevel()==TOWN || Game.CurrentLevel()==FOREST_1)){
						Buff.Rec("You continue into the forest. ");
						Buff.Print(true);
						if(Game.CurrentLevel()==TOWN){
							Game.GenerateLevel(FOREST_1);
						}
						else{
							Game.GenerateLevel(FOREST_2);
						}
						Q0();
					}
					else{
						if(TileInDirection(dir)->Col()==COLS-1 && (Game.CurrentLevel()==FOREST_1 || Game.CurrentLevel()==FOREST_2)){
							Buff.Rec("You continue back toward the town. ");
							Buff.Print(true);
							if(Game.CurrentLevel()==FOREST_1){
								Game.GenerateLevel(TOWN);
							}
							else{
								Game.GenerateLevel(FOREST_1);
							}
							Q0();
						}
						else{
							if(ActorInDirection(dir)!=NULL){
								if(ActorInDirection(dir)->IsAllied()){ //option to change places or push?
									Buff.Rec("You switch places with " + ActorInDirection(dir)->TheName() + ". ");
									Move(TileInDirection(dir)->Row(),TileInDirection(dir)->Col(),true);
									QS();
								}
								else{
									Attack(rand()%2,ActorInDirection(dir));
								}
							}
							else{
								if(TileInDirection(dir)->IsWalkable()){
									if(TileInDirection(dir)->ItemPtr()!=NULL){
										Buff.Rec("You see " + TileInDirection(dir)->ItemPtr()->AName() + ". ");
									}
									Move(TileInDirection(dir)->Row(),TileInDirection(dir)->Col());
									QS();
								}
								else{
									Buff.Rec("There is " + TileInDirection(dir)->AName() + " in the way. ");
									Q0();
								}
							}
						}
					}
				}
				else{
					Buff.Rec("Debug message 1. "); //old code. pretty sure this can't happen.
					Q0();
				}
				break;
			}
		case '5':
		case '.':
			{
				if(has_attr[BURNING]){
					has_attr[BURNING]=0;
					Buff.Rec("You extinguish the flames. ");
				}
				if(Map.TilePtr(row,col)->ItemPtr()!=NULL){
					Buff.Rec("You see " + Map.TilePtr(row,col)->ItemPtr()->AName() + ". ");
				}
				QS();
				break;
			}
		case 'd':
			{
				if(inv.empty()){
					Buff.Rec("You have nothing to drop. ");
					Q0();
				}
				else{
					Item* temp = SelectItem("Drop which item? ");
					if(temp!=NULL){
						Buff.Rec("You drop " + temp->TheName() + ". ");
						LoseItem(temp);
						Map.TilePtr(row,col)->GetItem(temp);
						Q1();
					}
					else{
						Q0();
					}
				}
				break;
			}
		case 'g':
		case ';':
			{
				if(Map.TilePtr(row,col)->ItemPtr()==NULL){
					Buff.Rec("There's nothing here to pick up. ");
					Q0();
				}
				else{
					Item* temp = Map.TilePtr(row,col)->ItemPtr();
					temp->SetPos(-1,-1);
					Map.TilePtr(row,col)->LoseItem();
					Buff.Rec("You pick up " + temp->TheName() + ". ");
					GetItem(temp);
					Q1();
				}
				break;
			}
		case '>':
			{
				if(Map.TilePtr(row,col)->ID()==4){ //idnum-specific
					Buff.Rec("You walk down the stairs. ");
					Buff.Print(true);
					LevelType nextlevel;
					switch(Game.CurrentLevel()){
						case FOREST_2:
							nextlevel = CAVE_1;
							break;
						case CAVE_1:
							nextlevel = CAVE_2;
							break;
						case CAVE_2:
							nextlevel = CAVE_3;
							break;
						default:
							nextlevel = FOREST_2;
							break;
					}
					Game.GenerateLevel(nextlevel);
					Q0();
				}
				else{
					Buff.Rec("There are no down stairs here. ");
					Q0();
				}
				break;
			}
		case '<':
			{
				if(Map.TilePtr(row,col)->ID()==7){ //idnum-specific
					Buff.Rec("You walk up the stairs. ");
					Buff.Print(true);
					LevelType nextlevel;
					switch(Game.CurrentLevel()){
						case CAVE_3:
							nextlevel = CAVE_2;
							break;
						case CAVE_2:
							nextlevel = CAVE_1;
							break;
						case CAVE_1:
							nextlevel = FOREST_2;
							break;
						default:
							nextlevel = FOREST_1;
							break;
					}
					Game.GenerateLevel(nextlevel);
					Q0();
				}
				else{
					Buff.Rec("There are no up stairs here. ");
					Q0();
				}
				break;
			}
		case 'W':
			{
				if(weapon!=NULL && weapon->ID()==30){
					if(Game.CurrentLevel()==TOWN){
						Buff.Rec("The ground shakes. ");
						Buff.Print(true);
						Buff.Rec("The dead rise from the ground all around you. ");
						Buff.Print(true);
						Buff.Rec("The living are slain. The dead are yours to command. ");
						Buff.Print(true);
						Buff.Rec("Victory is yours. Congratulations! ");
						Buff.Print(true);
						endwin();
						exit(0);
					}
					else{
						for(int i=0;i<ROWS;++i){
							for(int j=0;j<COLS;++j){
								if(Map.ActorPtr(i,j)!=NULL && Map.ActorPtr(i,j)->HasAttr(UNDEAD)){
									Map.ActorPtr(i,j)->MakeAllied(true);
								}
							}
						}
						Buff.Rec("You activate the Moonscythe. ");
						Buff.Rec("Every undead being in the area bows before you. ");
						Q1();
					}
				}
				else{
				Buff.Rec("Nothing happens. ");
				Q0();
				}
				break;
			}
		case 'l':
			{
				Buff.Now("Move cursor to look around. ");
				Cursor();
				int lr=row;
				int lc=col;
				while(inchar!=27 && inchar!=' '){
					top_panel(mappan);
					update_panels();
					doupdate();
					inchar = getch();
					switch(inchar){
						case '7':
							if(lr==0){
								if(lc!=0){
									--lc;
								}
							}
							else{
								if(lc==0){
									--lr;
								}
								else{
									--lr;
									--lc;
								}
							}
							break;
						case '8':
						case 259:
							if(lr!=0){
								--lr;
							}
							break;
						case '9':
							if(lr==0){
								if(lc!=COLS-1){
									++lc;
								}
							}
							else{
								if(lc==COLS-1){
									--lr;
								}
								else{
									--lr;
									++lc;
								}
							}
							break;
						case '4':
						case 260:
							if(lc!=0){
								--lc;
							}
							break;
						case '6':
						case 261:
							if(lc!=COLS-1){
								++lc;
							}
							break;
						case '1':
							if(lr==ROWS-1){
								if(lc!=0){
									--lc;
								}
							}
							else{
								if(lc==0){
									++lr;
								}
								else{
									++lr;
									--lc;
								}
							}
							break;
						case '2':
						case 258:
							if(lr!=ROWS-1){
								++lr;
							}
							break;
						case '3':
							if(lr==ROWS-1){
								if(lc!=COLS-1){
									++lc;
								}
							}
							else{
								if(lc==COLS-1){
									++lr;
								}
								else{
									++lr;
									++lc;
								}
							}
							break;
						default:
							break;
					}
					if(lr==row && lc==col){
						if(inchar!=27 && inchar!=' '){
							Buff.Now("You're standing here. ");
						}
					}
					else{
						if(Map.TilePtr(lr,lc)->HasBeenSeen()){
							if(Map.ActorPtr(lr,lc)!=NULL && CanSee(lr,lc)){
								Buff.Now("You see " + Map.ActorPtr(lr,lc)->AName() + ". ");
							}
							else{
								if(Map.TilePtr(lr,lc)->ItemPtr()!=NULL){
									Buff.Now("You see " + Map.TilePtr(lr,lc)->ItemPtr()->AName() + ". ");
								}
								else{
									Buff.Now("You see " + Map.TilePtr(lr,lc)->AName() + ". ");
								}
							}
						}
						else{
							Buff.Now("");
						}
					}
					Map.TilePtr(lr,lc)->Cursor();
					top_panel(mappan);
					update_panels();
					doupdate();
				}
				Q0();
				break;
			}
		case 'i':
			{
				if(inv.empty()){
					Buff.Rec("You have nothing in your pack. ");
				}
				else{
					DisplayInv();
					top_panel(mappan);
					update_panels();
					Buff.Now("In your pack: ");
					getch();
				}
				Q0();
				break;
			}
		case 'P':
			{
				wattron(mapwin,A_BOLD);
				wmove(mapwin,0,0);
				wprintw(mapwin,"                                                                  ");
				for(int j=1;j<ROWS-1;++j){
					for(int k=0;k<COLS;++k){
						wmove(mapwin,j,k);
						waddch(mapwin,' ');
					}
				}
				for(int i=1;i<21;++i){
					wmove(mapwin,i,0);
					wprintw(mapwin,(char *)Buff.Printed(i).c_str());
				}
				wmove(mapwin,21,0);
				wprintw(mapwin,"                                                                  ");
				top_panel(mappan);
				update_panels();
				Buff.Now("Previous messages: ");
				getch();
				wattroff(mapwin,A_BOLD);
				Q0();
				break;
			}
/*		case 'S':
			{
				if(minions.empty()){
					Buff.Rec("You don't have any minions! ");
					Q0();
					break;
				}
				int num=0;
				if(minions.size() > 1){
					Buff.Now("Summon which of your undead minions?(1-" + IntToString(minions.size()) + ") ");
					inchar = getch();
					switch(inchar){
						case '1':
							num = 0;
							break;
						case '2':
							num = 1;
							break;
						case '3':
							num = 2;
							break;
						case '4':
							num = 3;
							break;
						default:
							num = 99;
							break;
					}
				}
				if(num < minions.size()){
					Tile* ptr = GetTarget("Choose a location, then press 't' or Enter. ");
					if(ptr!=NULL && ptr->IsWalkable() && Map.ActorPtr(ptr->Row(),ptr->Col())==NULL){
						minions[num]->Summon(ptr->Row(),ptr->Col());
						Q1();
					}
					else{
						Q0();
					}
				}
				else{
					Q0();
				}
				break;
			}*/
		case 265: //F1 through F4
		case 266:
		case 267:
		case 268:
		case 'A':
		case 'B':
		case 'C':
		case 'D':
			{
				if(inchar>='A' && inchar<='D'){ //65 to 68
					inchar += 200;
				}
				if(inchar - 264 <= minions.size() && minions[inchar-265]->Row()==-1){
					Tile* ptr = GetTarget("Choose a location, then press 't' or Enter. ");
					if(ptr!=NULL && ptr->IsWalkable() && Map.ActorPtr(ptr->Row(),ptr->Col())==NULL){
						minions[inchar-265]->Summon(ptr->Row(),ptr->Col());
						Q1();
					}
					else{
						Q0();
					}
				}
				else{
					Q0();
				}
				break;
			}
		case 'N':
			{
				Buff.Now("Give which of your minions a nickname? ");
				Tile* ptr = GetTarget("Choose one of your minions, then press 't' or Enter. ");
				if(ptr!=NULL){
					Actor* minion = Map.ActorPtr(ptr->Row(),ptr->Col());
					if(minion!=NULL && minion->IsAllied()){
						Buff.Now("Enter the nickname: ");
						minion->SetNickname(EnterString(msgwin,12));
					}
				}
				Q0();
				break;
			}
		case 'F':
			{
				Item* ptr = SelectItem("Feed which corpse to your minions? ");
				if(ptr!=NULL && ptr->ID()==29){
					LoseItem(ptr);
					for(int i=0;i<minions.size();++i){
						minions[i]->curhp += minions[i]->maxhp/2;
						if(minions[i]->curhp > minions[i]->maxhp){
							minions[i]->curhp = minions[i]->maxhp;
						}
					}
					Q1();
				}
				else{
					Q0();
				}
				break;
			}
		case 'Z':
			{
				if(spells.size() > 0){
					SpellType temp = SelectSpell("Cast which spell? ");
					if(temp!=NO_SPELL){
						CastSpell(temp);
					}
					else{
						Q0();
					}
				}
				else{
					Buff.Rec("You don't know any spells! ");
					Q0();
				}
				break;
			}
		case 'S':
			{
				int dir = GetDirection();
				if(dir!=0){
					if(ActorInDirection(dir)!=NULL){
						switch(ActorInDirection(dir)->ID()){
							case 13:
								Buff.Rec("The guard nods at you. ");
								break;
							case 14:
								Buff.Rec("The townsperson looks at you strangely. ");
								break;
							case 15:
								Buff.Now("\"So, want to buy it?\" (Y/N): ");
								inchar = getch();
								if(inchar=='y' || inchar=='Y'){
									if(Player.Gold() >= 1000){
										Player.LoseGold(1000);
										int i=Roll(1,6);
										if(i==4){
											i=7;
										}
										LoadedItem[21+i].CloneSelf(Player.Row(),Player.Col(),true);
										Buff.Rec("He shoves a bundled package into your hands. ");
										Buff.Rec("\"Come back if you want something else.\" ");
									}
									else{
										Buff.Rec("\"Hey, what are you trying to pull here? You don't have the gold!\" ");
									}
								}
								else{
									Buff.Rec("\"Suit yourself.\" ");
								}
								break;
							case 16:
								Buff.Now("\"How much would you like to donate?\" ");
								{
								int i =EnterInt(msgwin);
								if(i > 0){
									LoseGold(i);
									Buff.Rec("\"Thank you. Here are some books I've enjoyed.\" ");
									if(i >= 100){
										Buff.Rec("You learn a few spells from these books. ");
										GainSpell(R_MUMMY);
										GainSpell(R_WIGHT);
										GainSpell(R_SHADE);
										GainSpell(R_SPIRIT);
										GainSpell(GRIM_FEAST);
										GainSpell(DETECT_LIVING);
										GainSpell(DETECT_UNDEAD);
									}
									if(i >= 500){
										Buff.Rec("You learn a few more spells. ");
										GainSpell(R_WRAITH);
										GainSpell(R_BANSHEE);
										GainSpell(R_SPECTRE);
										GainSpell(R_DRAUGR);
										GainSpell(HIDE_FROM_UNDEAD);
									}
								}
								}
								break;
							default:
								Buff.Rec("You receive no response. ");
								break;
						}
						Q1();
					}
					else{
						Q0();
					}
				}
				else{
					Q0();
				}
				break;
			}
		case 'w':
			{
				Item* ptr = SelectItem("Which item? ");
				if(ptr!=NULL && ptr->Symbol()=='|'){
					if(weapon!=NULL){
						GetItem(weapon);
					}
					weapon = ptr;
					LoseItem(ptr);
				}
				Q1();
				break;
			}
		case 'R':
			{
				Buff.Now("Recall which of your minions? [A-D] ");
				inchar = getch();
				if(inchar -65 < minions.size() && minions[inchar-65]->Row()!=-1){
					minions[inchar-65]->Unsummon();
					Buff.Rec("You recall "+minions[inchar-65]->TheName()+ ". ");
				Q1();
				}
				else{
				Q0();
				}
				break;
			}
		case '=':
			{
				prw(mapwin);
				wattron(mapwin,A_BOLD);
				while(inchar!=27 && inchar!=KEY_ENTER && inchar!=' '){
					for(int j=0;j<COLS;++j){
						wmove(mapwin,0,j);
						waddch(mapwin,' ');
					}
					for(int i=1;i<ROWS;++i){
						for(int j=0;j<COLS;++j){
							wmove(mapwin,i,j);
							waddch(mapwin,' ');
						}
					}
					for(int j=0;j<COLS;++j){
						wmove(mapwin,4,j);
						waddch(mapwin,' ');
					}
					wmove(mapwin,1,0);
					wprintw(mapwin,"[a] Automatically recall minions past a certain distance");
					if(Game.RecallDistance() == 0){
						mpr(1,58,"no ");
					}
					else{
						mpr(1,58,IntToString(Game.RecallDistance()));
					}
					wmove(mapwin,2,0);
//					wprintw(mapwin,"[b] Always visually differentiate between minions");
					wmove(mapwin,2,58);
//					if(Game.Option(DIFFERENT_COLORS)){
//						wprintw(mapwin,"yes");
//					}
//					else{
//						wprintw(mapwin,"no");
//					}
//					wmove(mapwin,3,0);
//					wprintw(mapwin,"[c] new option");
//					wmove(mapwin,3,58);
//					if(Game.Option(LAST_TARGET)){
//						wprintw(mapwin,"yes");
//					}
//					else{
//						wprintw(mapwin,"no");
//					}
					DisplayStats();
					prw(mapwin);
					top_panel(mappan);
					update_panels();
					Buff.Now("Change which option? ");
					inchar = getch();
					switch(inchar){
						case 'a':
							{
								mpr(1,58,"   ");
								wmove(mapwin,1,58);
								top_panel(mappan);
								update_panels();
								doupdate();
								int i = EnterInt(mapwin,2);
								top_panel(msgpan);
								if(i > 2){
									Game.SetDistance(i);
								}
								else{
									Game.SetDistance(0);
								}
								break;
							}
						case 'b':
//							Game.ToggleOption(DIFFERENT_COLORS);
							break;
						case 'c':
							break;
						default:
							break;
					}
				}
				wattroff(mapwin,A_BOLD);
				Q0();
				break;
			}
		case '?':
			{
				Buff.Rec("Help will soon be available. Try asking in #rgrd. ");
				Q0();
				break;
			}
		case 'Q':
			{
				Buff.Now("Really quit without saving? Press '*' to quit: ");
				inchar = getch();
				if(inchar=='*'){
					wmove(msgwin,0,0);
					wprintw(msgwin,"                                                                  ");
					wmove(msgwin,0,0);
					top_panel(msgpan);
					update_panels();
					doupdate();
					endwin();
					exit(0);
				}
				else{
					Q0();
				}
				break;
			}
		case 'X':
			if(false){
				wattron(mapwin,A_BOLD);
				bool done=false;
				while(done==false && inchar!=27 && inchar!=KEY_ENTER && inchar!=' '){
					prw(mapwin);
					for(int j=0;j<COLS;++j){
						wmove(mapwin,0,j);
						waddch(mapwin,' ');
					}
					for(int i=1;i<ROWS;++i){
						for(int j=0;j<COLS;++j){
							wmove(mapwin,i,j);
							waddch(mapwin,' ');
						}
					}
					for(int j=0;j<COLS;++j){
						wmove(mapwin,7,j);
						waddch(mapwin,' ');
					}
					mpr(1,0,"[a] Restore health");
					mpr(2,0,"[b] Toggle invulnerability");
					mpr(3,0,"[c] View the entire map");
					mpr(4,0,"[d] Generate the next level");
					mpr(5,0,"[e] Gain several ATTRs");
					mpr(6,0,"[f] Lose several ATTRs");
					DisplayStats();
					top_panel(mappan);
					update_panels();
					Buff.Now("Cheat menu: ");
					inchar = getch();
					switch(inchar){
						case 'a':
							{
								curhp=maxhp;
								curmp=maxmp;
								for(int i=0;i<minions.size();++i){
									minions[i]->curhp = minions[i]->maxhp;
								}
								done=true;
								break;
							}
						case 'b':
							{
/*								if(has_attr[INVULNERABLE]){
									Buff.Rec("Invulnerability off. ");
									has_attr[INVULNERABLE]=0;
								}
								else{
									Buff.Rec("Invulnerability on. ");
									has_attr[INVULNERABLE]++;
								}*/
								done=true;
								break;
							}
						case 'c':
							{
								for(int i=0;i<ROWS;++i){
									for(int j=0;j<COLS;++j){
										Map.TilePtr(i,j)->Draw(true,true);
									}
								}
								top_panel(msgpan);
								update_panels();
								doupdate();
								getch();
								done=true;
								break;
							}
						case 'd':
							{
								LevelType nextlevel = LevelType(int(Game.CurrentLevel())+1);
								if(Game.CurrentLevel()==CAVE_3){
									nextlevel = TOWN;
								}
								Game.GenerateLevel(nextlevel);
								done=true;
								break;
							}
						case 'e':
							{
								GainAttr(RESIST_FIRE);
								GainAttr(RESIST_COLD);
								GainAttr(RESIST_ELEC);
								GainAttr(BURNING);
								done=true;
								break;
							}
						case 'f':
							{
								LoseAttr(RESIST_FIRE);
								LoseAttr(RESIST_COLD);
								LoseAttr(RESIST_ELEC);
								LoseAttr(BURNING);
								done=true;
								break;
							}
						default:
							break;
					}
				}
				wattroff(mapwin,A_BOLD);
				//Q0();
//				break;
			}
			Q0(); //
			break;
		case ' ':
			{
				Q0();
				break;
			}
		default:
			{
//				Buff.Rec("Command ( " + IntToString(inchar) + " ) not recognized. ");
				Buff.Rec("Press '?' for help. ");
				Q0();
				break;
			}
	}
}
void Actor::InpAI(){
	if(allied){
		int distance=999;
		vector<Actor*> foes;
		for(int i=0;i<ROWS;++i){ //i duplicated all this code because i didn't remember the symbol for XOR
			for(int j=0;j<COLS;++j){ //i'd just XOR your_team and my_team, basically
				if(Map.ActorPtr(i,j)!=NULL && !Map.ActorPtr(i,j)->IsAllied() && DistanceFrom(i,j)<=distance && CanSee(i,j)){
					if(DistanceFrom(i,j) < distance){
						foes.clear();
						distance = DistanceFrom(i,j);
					}
					foes.insert(foes.end(),Map.ActorPtr(i,j));
				}
			}
		}
		if(!foes.empty()){
			if(foes.size()==1){
				target = foes[0];
			}
			else{
				target = foes[rand()%foes.size()];
			}
			player_seen = Map.TilePtr(target->Row(),target->Col());
		}
		else{
			if(player_seen!=NULL && DistanceFrom(player_seen)==0){
				player_seen=NULL;
			}
			if(target!=NULL && target->IsAllied()){
				target=NULL;
			}
		}
	}
	else{
		int distance=999;
		vector<Actor*> foes;
		for(int i=0;i<ROWS;++i){
			for(int j=0;j<COLS;++j){
				if(Map.ActorPtr(i,j)!=NULL && Map.ActorPtr(i,j)->IsAllied() && DistanceFrom(i,j)<=distance && (!Map.ActorPtr(i,j)->HasAttr(HIDDEN_FROM_UNDEAD) || !has_attr[UNDEAD]) && CanSee(i,j)){
					if(DistanceFrom(i,j) < distance){
						foes.clear();
						distance = DistanceFrom(i,j);
					}
					foes.insert(foes.end(),Map.ActorPtr(i,j));
				}
			}
		}
		if(!foes.empty()){
			if(foes.size()==1){
				target = foes[0];
			}
			else{
				target = foes[rand()%foes.size()];
			}
			player_seen = Map.TilePtr(target->Row(),target->Col());
		}
		else{
			if(player_seen!=NULL && DistanceFrom(player_seen)==0){
				player_seen=NULL;
			}
		}
	}
	bool path_step=false;
	switch(AI){
		case STANDARD:
			if(allied && Game.RecallDistance()!=0 && DistanceFrom(&Player)>Game.RecallDistance()){
				Buff.Rec("You recall " + TheName() + ". ");
				Unsummon();
				break;
			}
			if(target!=NULL){
				if(CanSee(target)){
					if(DistanceFrom(target)==1){
						Attack(0,target);
					}
					else{
						AI_Step(target);
						QS();
					}
				}
				else{
					if(player_seen!=NULL){
						if(DistanceFrom(player_seen)==1 && Map.ActorPtr(player_seen->Row(),player_seen->Col())!=NULL){
							Move(player_seen->Row(),player_seen->Col(),true);
							QS();
							player_seen=NULL;
						}
						else{
							if(AI_Step(player_seen)){
								QS();
								if(DistanceFrom(player_seen)==0){
									player_seen=NULL;
								}
							}
							else{
								//maybe try another push here?
								QS();
							}
						}
					}
					else{
						if(DistanceFrom(target) <= 2){
							AI_Step(target);
							QS();
						}
						else{
							target=NULL;
							QS();
						}
					}
				}
			}
			else{
				if(allied && DistanceFrom(&Player)>1){
					if(DistanceFrom(&Player)<=4 || CanSee(&Player)){
						AI_Step(&Player);
					}
				}
				QS();
			}
			break;
		case NECRO:
			if(target!=NULL){
				if(CanSee(target)){
					if(DistanceFrom(&Player)<=8 && CanSee(&Player) && !has_attr[GESTURED]){
						int i=Roll(1,7);
						switch(i){
						case 1:
						case 2:
							Buff.Rec("The necromancer speaks: \"I will make your bones dance!\" ");
							break;
						case 3:
						case 4:
							Buff.Rec("The necromancer speaks: \"The Moonscythe will be mine!\" ");
							break;
						case 5:
						case 6:
							Buff.Rec("The necromancer spits in your direction. ");
							break;
						case 7:
							Buff.Rec("The necromancer speaks: \"Come get some.\" ");
							break;
						}
						has_attr[GESTURED]++;
					}
/*					if(DistanceFrom(target)==1){
						Attack(0,target);
					}
					else{
						AI_Step(target);
						QS();
					}*/
					Actor* temp=NULL;
					for(int i=0;i<minions.size();++i){
						if(minions[i]->Row()==-1){
							temp = minions[i];
						}
					}
					if(temp!=NULL){
						bool done=false;
						int dir;
						while(!done){
							dir = Roll(1,8);
							if(dir==5){ dir=9; }
							if(TileInDirection(dir)->IsWalkable()){
								done=true;
							}
						}
						temp->Summon(TileInDirection(dir)->Row(),TileInDirection(dir)->Col());
						Buff.Rec("The necromancer summons " + temp->AName() + ". ");
					}
					Q1();
				}
				else{
					if(player_seen!=NULL){
						if(DistanceFrom(player_seen)==1 && Map.ActorPtr(player_seen->Row(),player_seen->Col())!=NULL){
							Move(player_seen->Row(),player_seen->Col(),true);
							QS();
							player_seen=NULL;
						}
						else{
							if(AI_Step(player_seen)){
								QS();
								if(DistanceFrom(player_seen)==0){
									player_seen=NULL;
								}
							}
							else{
								//maybe try another push here?
								QS();
							}
						}
					}
					else{
						if(DistanceFrom(target) <= 2){
							AI_Step(target);
							QS();
						}
						else{
							target=NULL;
							QS();
						}
					}
				}
			}
			else{
				QS();
			}
			break;
		case TOWNSFOLK:
			if(Game.Alarm() && target!=NULL && Roll(1,8)!=8){
				AI_Step(target,true); //flee from closest enemy
				target=NULL;
				QS();
			}
			else{
				int dir = Roll(1,8);
				if(dir==5){
					dir=9;
				}
				AI_Step(TileInDirection(dir));
				target=NULL;
				QS();
			}
			break;
		case GUARD:
			if(Game.Alarm()){
				if(target!=NULL){
					if(DistanceFrom(target)==1){
						Attack(0,target);
					}
					else{
						AI_Step(target);
						QS();
					}
				}
				else{
					AI_Step(&Player);
					QS();
				}
			}
			else{
				QS();
			}
			break;
		case SHADY_CHARACTER:
			if(Game.Alarm() && target!=NULL){
				AI_Step(target,true); //flee from closest enemy
				QS();
			}
			else{
				if(DistanceFrom(&Player)<=8 && CanSee(&Player) && !has_attr[GESTURED]){
					Buff.Rec("The shady character gestures for you to come over. ");
					has_attr[GESTURED]++;
				}
				else{
					if(DistanceFrom(&Player)==1 && has_attr[GESTURED] < 2){
						Buff.Rec("\"Wanna buy a weapon? Only 1000 gold pieces!\" ");
						has_attr[GESTURED] = 2;
					}
				}
				QS();
			}
			break;
		case TRINKET_SALESMAN:
			if(Game.Alarm() && target!=NULL){
				AI_Step(target,true); //flee from closest enemy
				QS();
			}
			else{
				if(DistanceFrom(&Player)<=8 && CanSee(&Player) && !has_attr[GESTURED]){
					Buff.Rec("The shop owner speaks: \"Hail and well met! Have a look at my trinkets.\" ");
					has_attr[GESTURED]++;
				}
				QS();
			}
			break;
		case LIBRARIAN:
			if(DistanceFrom(&Player)<=5 && CanSee(&Player) && !has_attr[GESTURED]){
				Buff.Rec("\"Oh, hello there. Are you here to find something?\" ");
				has_attr[GESTURED]++;
			}
			QS();
			break;
		default:
			QS();
			break;
	}
}
bool Actor::AI_Step(Object* ptr,bool flee){
	bool result = false;
	int rowchange = 0;
	int colchange = 0;
	if(ptr->Row() < row){
		rowchange = -1;
	}
	else{
		if(ptr->Row() > row){
			rowchange = 1;
		}
	}
	if(ptr->Col() < col){
		colchange = -1;
	}
	else{
		if(ptr->Col() > col){
			colchange = 1;
		}
	}
	if(flee){
		rowchange = -rowchange;
		colchange = -colchange;
	}
	// yeah, this *might* have been easier to read before I magicked the formula, but it was sooo many lines
	//	(the basic idea is that if you want to move north, it tries N, then NW and NE(in random order), then W and E(in random order))
	int i = abs(rowchange + colchange) % 2;
	if(Roll(1,2) == 1){ //don't always try the same direction first
		result = AI_MoveOrOpen(row + rowchange,col + colchange)
				|| AI_MoveOrOpen(row + rowchange - i * (colchange * colchange),col + i * (colchange - rowchange * rowchange))
				|| AI_MoveOrOpen(row + i * (rowchange + colchange * colchange),col + colchange + i * (rowchange * rowchange))
				|| AI_MoveOrOpen(row - rowchange + i * (rowchange + colchange),col + colchange + i * (-colchange + rowchange))
				|| AI_MoveOrOpen(row + rowchange - i * (rowchange + colchange),col - colchange + i * (colchange - rowchange));
	}
	else{
		result = AI_MoveOrOpen(row + rowchange,col + colchange)
				|| AI_MoveOrOpen(row + i * (rowchange + colchange * colchange),col + colchange + i * (rowchange * rowchange))
				|| AI_MoveOrOpen(row + rowchange - i * (colchange * colchange),col + i * (colchange - rowchange * rowchange))
				|| AI_MoveOrOpen(row + rowchange - i * (rowchange + colchange),col - colchange + i * (colchange - rowchange))
				|| AI_MoveOrOpen(row - rowchange + i * (rowchange + colchange),col + colchange + i *(-colchange + rowchange));
	}
	return result;
}
bool Actor::AI_MoveOrOpen(int r,int c){ //...
	if((Map.TilePtr(r,c)->IsWalkable() || has_attr[INCORPOREAL]) && Map.ActorPtr(r,c)==NULL){
		if(r==0 || c==0 || r==ROWS-1 || c==COLS-1){
			return false; //can't move to edge of map
		}
		Move(r,c);
		return true;
	}
	return false;
}
bool Actor::Attack(int attack_idx,Actor* ptr){
	bool hit = ptr->IsHit(0);
	attack_idx = attack[attack_idx]; //changes the local attack index to a global one
	if(attack_idx==7 || attack_idx==18){
		hit=true; //hacky. these 2 are area attacks and always hit.  notice that neither of these attacks contains '*'. this could
	}									//be used to determine which attacks are area attacks.
	string temp = LoadedAttack[attack_idx].desc + ". "; //^--code i never changed--^
	if(hit){
		bool crit=false;
		int symbol = temp.find('&');
		if(symbol!=string::npos){
			temp = temp.substr(0,symbol) + TheName() + temp.substr(symbol+1,100);
		}
		symbol = temp.find('^');
		if(symbol!=string::npos){
			if(Roll(1,20)==20){
				crit=true;
				temp = temp.substr(0,symbol) + "critically " + temp.substr(symbol+1,100);
			}
			else{
				temp = temp.substr(0,symbol) + temp.substr(symbol+1,100);
			}
		}
		symbol = temp.find('*');
		if(symbol!=string::npos){
			temp = temp.substr(0,symbol) + ptr->TheName() + temp.substr(symbol+1,100);
		}
		Buff.Rec(temp);
		int dice = LoadedAttack[attack_idx].dice;
		int dmg;
		if(crit){
			dmg = dice + Roll(1,dice);
		}
		else{
			dmg = dice;
		}
		ptr->TakeDamage(LoadedAttack[attack_idx].type,dmg,this);
	}
	else{
		if(temp == "& lunges forward and ^hits *. "){
			Buff.Rec(TheName() + " lunges forward and misses " + ptr->TheName() + ". ");
		}
		else{
			Buff.Rec(You("miss",true) + " " + ptr->TheName() + ". ");
		}
	}
	Q.Add(new Event(this,LoadedAttack[attack_idx].cost,MOVE));
	return hit;
}
void Actor::FireArrow(Tile* ptr){ //this entire function will be reworked once skeletons can have bows
	Actor* temp = LOELine(ptr,1);
	int mod = -30; //bows have base accuracy 45%
	if(temp!=NULL){
		if(temp->IsHit(mod)){
			if(Roll(1,20)==20){
				Buff.Rec(You("critically hit") + " " + temp->TheName() + " with an arrow. ");
				temp->TakeDamage(NORMAL,24,this); //dependent on arrow damage being 3 and dice being sixes
			}
			else{
				Buff.Rec(You("hit") + " " + temp->TheName() + " with an arrow. ");
				temp->TakeDamage(NORMAL,Roll(3,6),this);
			}
		}
		else{
			Buff.Rec(You("miss",true) + " " + temp->TheName() + " with an arrow. ");
		}
	}
	else{
		Buff.Rec(You("hit") + " " + ptr->TheName() + " with an arrow. ");
	}
	Q1();
}
bool Actor::IsHit(int plus_to_hit){
	bool result = true;
	if(Roll(1,100)+plus_to_hit <= 25){ //base hit chance is 75% now
		result = false;
	}
	return result;
}
void Actor::TakeDamage(DamageType type,int dmg,Actor* source){
	bool damage_dealt=false;
	switch(type){
		case NORMAL:
			{
				if(dmg > 0){
					curhp -= dmg;
					damage_dealt=true;
				}
				else{
					if(AI==HUMAN){
						Buff.Rec("You are undamaged. ");
					}
					else{
						Buff.Rec(TheName() + " is undamaged. ");
					}
				}
				break;
			}
		case WEAPON:
			{
				curhp -= dmg;
				damage_dealt=true;
				break;
			}
		case MAGIC:
			if(dmg > 0){
				curhp -= dmg;
				damage_dealt=true;
			}
			break;
		case FIRE:
			if(has_attr[IMMUNE_FIRE]){
				dmg=0;
			}
			if(dmg > 0){
				int div=1;
				for(int i=has_attr[RESIST_FIRE];i>0;--i){
					div = div * 2;
				}
				dmg = dmg / div;
				if(dmg > 0){
					curhp -= dmg;
					damage_dealt=true;
				} //else: print 'immune or resistant' message?
			}
			break;
		case COLD:
			if(has_attr[IMMUNE_COLD]){
				dmg=0;
			}
			if(dmg > 0){
				int div=1;
				for(int i=has_attr[RESIST_COLD];i>0;--i){
					div = div * 2;
				}
				dmg = dmg / div;
				if(dmg > 0){
					curhp -= dmg;
					damage_dealt=true;
				}
			}
			break;
		case ELEC:
			if(dmg > 0){
				int div=1;
				for(int i=has_attr[RESIST_ELEC];i>0;--i){
					div = div * 2;
				}
				dmg = dmg / div;
				if(dmg > 0){
					curhp -= dmg;
					damage_dealt=true;
				}
			}
			break;
		case HEAL:
			curhp += dmg;
			if(curhp > maxhp){
				curhp = maxhp;
			}
			break;
	}
	if(damage_dealt){
		if(source!=NULL){
			if(AI!=HUMAN){
				target = source;
			}
		}
	}
	if(curhp<=0){
		if(AI==HUMAN){
			Buff.Rec("You die. ");
			Buff.Print(true);
			wmove(msgwin,0,0);
			wprintw(msgwin,"                                                                  ");
			wmove(msgwin,0,0);
			top_panel(msgpan);
			update_panels();
			doupdate();
			endwin();
			exit(0);
		}
		else{
			if(allied){
				if(Player.PayMP(2)){
					Buff.Rec(TheName() + " has been defeated. You recall " + TheName() + ". ");
					curhp=0;
					Unsummon();
					return;
				}
				else{
					Buff.Rec("You don't have enough power to recall " + TheName() + ". ");
				}
			}
			if(has_attr[UNDEAD]){
				Buff.Rec(TheName() + " is destroyed. ");
			}
			else{
				Buff.Rec(TheName() + " dies. ");
				LoadedItem[29].CloneSelf(row,col);
				if(Game.CurrentLevel()==TOWN){
					Game.RaiseTheAlarm();
				}
				Player.GainGold(Roll(4,20));
			}
			if(allied){
				Player.RemoveMinion(this);
			}
			for(int i=0;i<minions.size();++i){
				if(minions[i]->Row()==-1){
					delete minions[i];
				}
			}
			Q.KillEvents(this,ANY_EVENT);
			Map.RemoveTargets(this);
			Map.DelActorPtr(row,col);
			delete this;
		}
	}
}
SpellType Actor::SelectSpell(string in){
	top_panel(mappan);
	prw(mapwin);
	wattron(mapwin,A_BOLD);
	for(int i=0;i<ROWS;++i){
		for(int j=0;j<COLS;++j){
			mpr(i,j," ");
		}
	}
	mpr(1,0,"    - Spell -                                         - Cost -    ");
	for(int i=0;i<spells.size();++i){
		mpr(i+2,0,string("[") + char(i+97) + "] " + SpellName(spells[i]));
		mpr(i+2,56,IntToString(SpellCost(spells[i])) + " MP");
	}
	Buff.Now(in);
	int inchar = '[';
	while(inchar!=27 && inchar!=' '){
		inchar = getch();
		if(inchar>='a' && inchar-96<=spells.size()){
			wattroff(mapwin,A_BOLD);
			return spells[inchar-97];
		}
	}
	wattroff(mapwin,A_BOLD);
	return NO_SPELL;
}
Item* Actor::ItemPtr(int num){
	return inv[num-1];
}
void Actor::LoseItem(Item* ptr){
	int i;
	for(i=0;i<inv.size();++i){
		if(inv[i] == ptr){
			break;
		}
	}
	if(i != inv.size()){
		inv.erase(inv.begin()+i,inv.begin()+i+1);
	}
}
void Actor::DisplayInv(){
	wattron(mapwin,A_BOLD);
	for(int j=0;j<COLS;++j){
		wmove(mapwin,0,j);
		waddch(mapwin,' ');
	}
	for(int i=1;i<ROWS;++i){
		for(int j=0;j<COLS;++j){
			wmove(mapwin,i,j);
			waddch(mapwin,' ');
		}
	}
	int num=1;
	for(;num<=20 && num-1<inv.size();++num){
		wmove(mapwin,num,0);
		waddch(mapwin,'[');
		waddch(mapwin,num+96); //'a' is 97
		waddch(mapwin,']');
		waddch(mapwin,' ');
		wprintw(mapwin,((char *)inv[num-1]->AName().c_str()));
	}
	for(int j=0;j<COLS;++j){
		wmove(mapwin,num,j);
		waddch(mapwin,' ');
	}
	wattroff(mapwin,A_BOLD);
}
Item* Actor::SelectItem(string in){
	int inchar = 'z';
	int total = inv.size();
	while(inchar!=27 && inchar!=' '){
		DisplayInv();
		top_panel(mappan);
		update_panels();
		Buff.Now(in);
		inchar = getch();
		if(inchar>='a' && inchar<='t'){
			if(total>=inchar-96){ //'a' is 97
				return ItemPtr(inchar-96);
			}
		}
	}
	return NULL;
}
void Actor::DisplayEquipment(){
}
void Actor::DisplayStats(bool lower_only){
	prw(statwin);
	if(!lower_only){
/*		wmove(statwin,1,0);
		if(curhp >= 75){
			wprintw(statwin,"HP:%d/%d  ",curhp,maxhp);
		}
		else{
			if(curhp >= 50){
				wattron(statwin,COLOR_PAIR(COLOR_YELLOW));
				wprintw(statwin,"HP:%d/%d  ",curhp,maxhp);
				wattroff(statwin,COLOR_PAIR(COLOR_YELLOW));
			}
			else{
				if(curhp >= 25){
					wattron(statwin,COLOR_PAIR(COLOR_MAGENTA));
					wprintw(statwin,"HP:%d/%d  ",curhp,maxhp);
					wattroff(statwin,COLOR_PAIR(COLOR_MAGENTA));
				}
				else{
					wattron(statwin,COLOR_PAIR(COLOR_RED));
					wprintw(statwin,"HP:%d/%d  ",curhp,maxhp);
					wattroff(statwin,COLOR_PAIR(COLOR_RED));
				}
			}
		}*/
		mpr(1,0,"HP:" + IntToString((curhp*100)/maxhp,3) + "%%");
		string str = "MP:" + IntToString(curmp) + "/" + IntToString(maxmp);
		mpr(2,0,str);
		mpr(3,0,"            ");
		mpr(3,0,"$:" + IntToString(gold));
		if(weapon!=NULL){
			if(weapon->ID()==30){
				mpr(4,0,"W:Moonscythe ");
			}
			else{
				mpr(4,0,weapon->Name());
				mpr(5,0,"             ");
			}
		}
//		mpr(4,0,"W:");
//		mpr(5,0,"T:");
	}
	for(int i=6;i<=22;++i){
		mpr(i,0,"            ");
	}
	mpr(7,0," - Minions -",WHITE);
	if(!minions.empty()){
		for(int i=0;i<minions.size();++i){
			if(minions[i]->nickname != ""){
				mpr(8+(i*4),0,minions[i]->Nickname()); //whoops.
				mpr(9+(i*4),0,"the " + minions[i]->Name());
			}
			else{
				mpr(8+(i*4),0,"            ");
				mpr(9+(i*4),0,minions[i]->Name());
			}
			mpr(10+(i*4),3," HP: ");
			if(minions[i]->curhp == 0){ //there's gotta be a better way to write the following:
				wattron(statwin,COLOR_PAIR(RED));
				mpr(10+(i*4),0,"[ ]");
				wmove(statwin,10+(i*4),1);
				waddch(statwin,char(i+65)); //0 becomes 'A'...
				wattroff(statwin,COLOR_PAIR(RED));
			}
			else{
				if(minions[i]->Row() != -1){
					wattron(statwin,COLOR_PAIR(GREEN));
					mpr(10+(i*4),0,"[ ]");
					wmove(statwin,10+(i*4),1);
					waddch(statwin,char(i+65)); //0 becomes 'A'...
					wattroff(statwin,COLOR_PAIR(GREEN));
				}
				else{
					mpr(10+(i*4),0,"[ ]");
					wmove(statwin,10+(i*4),1);
					waddch(statwin,char(i+65)); //0 becomes 'A'...
				}
			}
			wmove(statwin,10+(i*4),8);
			pr(IntToString((minions[i]->curhp*100)/minions[i]->maxhp,3) + "%%");
			if(minions[i]->talisman != NULL){
				mpr(11+(i*4),0,minions[i]->talisman->ShortDesc());
			}
		}
	}
	top_panel(statpan);
	update_panels();
}
void Actor::RemoveTarget(Actor* ptr){
	if(target==ptr){
		target=NULL;
	}
}
Tile* Actor::GetTarget(string initial_msg){
	int inchar = 'z';
	int r,c;
//	if(target == NULL || !CanSee(target)){
		r = row;
		c = col;
		Cursor();
/*	}
	else{
		r = target->Row();
		c = target->Col();
		if(Game.Option(LAST_TARGET)){
			return Map.TilePtr(r,c);
		}
		target->Cursor();
	}*/
	Buff.Now(initial_msg);
	while(inchar!=27 && inchar!=KEY_ENTER && inchar!='t'){
		top_panel(mappan);
		update_panels();
		doupdate();
		inchar = getch();
		switch(inchar){
			case '7':
				if(r==0){
					if(c!=0){
						--c;
					}
				}
				else{
					if(c==0){
						--r;
					}
					else{
						--r;
						--c;
					}
				}
				break;
			case '8':
			case 259:
				if(r!=0){
					--r;
				}
				break;
			case '9':
				if(r==0){
					if(c!=COLS-1){
						++c;
					}
				}
				else{
					if(c==COLS-1){
						--r;
					}
					else{
						--r;
						++c;
					}
				}
				break;
			case '4':
			case 260:
				if(c!=0){
					--c;
				}
				break;
			case '6':
			case 261:
				if(c!=COLS-1){
					++c;
				}
				break;
			case '1':
				if(r==ROWS-1){
					if(c!=0){
						--c;
					}
				}
				else{
					if(c==0){
						++r;
					}
					else{
						++r;
						--c;
					}
				}
				break;
			case '2':
			case 258:
				if(r!=ROWS-1){
					++r;
				}
				break;
			case '3':
				if(r==ROWS-1){
					if(c!=COLS-1){
						++c;
					}
				}
				else{
					if(c==COLS-1){
						++r;
					}
					else{
						++r;
						++c;
					}
				}
				break;
			default:
				break;
		}
		if(inchar!=27){
			if(r == row && c == col){
				Buff.Now("You're standing here. ");
				Player.Cursor();
//				wnoutrefresh(mapwin);
				top_panel(mappan);
				update_panels();
				doupdate();
				inchar='z';
			}
			else{
				if(inchar!=KEY_ENTER && inchar!='t'){
					if(Map.TilePtr(r,c)->HasBeenSeen()){
						if(Map.ActorPtr(r,c)!=NULL && CanSee(r,c)){
							Buff.Now("You see " + Map.ActorPtr(r,c)->AName() + ". ");
						}
						else{
							if(Map.TilePtr(r,c)->ItemPtr()!=NULL){
								Buff.Now("You see " + Map.TilePtr(r,c)->ItemPtr()->AName() + ". ");
							}
							else{
								Buff.Now("You see " + Map.TilePtr(r,c)->AName() + ". ");
							}
						}
					}
					else{
						Buff.Now("Move cursor to choose target, then press 't' or Enter. ");
					}
					Map.TilePtr(r,c)->Cursor();
//					wnoutrefresh(mapwin);
					top_panel(mappan);
					update_panels();
					doupdate();
				}
			}
		}
	}
	if(inchar==27){
		return NULL;
	}
	else{
		if(HasLOS(r,c)){
			if(Map.ActorPtr(r,c)!=NULL){
				target = Map.ActorPtr(r,c);
			}
			return Map.TilePtr(r,c);
		}
		else{
			return NULL;
		}
	}
}
int Actor::GetDirection(bool orth){
	Buff.Now("Which direction? ");
	bool done=false;
	int inchar='z';
	int result = 0;
	while(!done && inchar!=27){
		inchar = getch();
		switch(inchar){
			case '7':
				if(!orth){
					result = 7;
					done=true;
				}
				break;
			case '8':
			case 259:
				result = 8;
				done=true;
				break;
			case '9':
				if(!orth){
					result = 9;
					done=true;
				}
				break;
			case '4':
			case 260:
				result = 4;
				done=true;
				break;
			case '6':
			case 261:
				result = 6;
				done=true;
				break;
			case '1':
				if(!orth){
					result = 1;
					done=true;
				}
				break;
			case '2':
			case 258:
				result = 2;
				done=true;
				break;
			case '3':
				if(!orth){
					result = 3;
					done=true;
				}
				break;
			default:
				break;
		}
	}
	return result;
}
Actor* Actor::ActorInDirection(int dir){
	switch(dir){
		case 7:
			return Map.ActorPtr(row-1,col-1);
		case 8:
			return Map.ActorPtr(row-1,col);
		case 9:
			return Map.ActorPtr(row-1,col+1);
		case 4:
			return Map.ActorPtr(row,col-1);
		case 5:
			return Map.ActorPtr(row,col);
		case 6:
			return Map.ActorPtr(row,col+1);
		case 1:
			return Map.ActorPtr(row+1,col-1);
		case 2:
			return Map.ActorPtr(row+1,col);
		case 3:
			return Map.ActorPtr(row+1,col+1);
		default:
			return NULL;
	}
}
Tile* Actor::TileInDirection(int dir){
	switch(dir){
		case 7:
			return Map.TilePtr(row-1,col-1);
		case 8:
			return Map.TilePtr(row-1,col);
		case 9:
			return Map.TilePtr(row-1,col+1);
		case 4:
			return Map.TilePtr(row,col-1);
		case 5:
			return Map.TilePtr(row,col);
		case 6:
			return Map.TilePtr(row,col+1);
		case 1:
			return Map.TilePtr(row+1,col-1);
		case 2:
			return Map.TilePtr(row+1,col);
		case 3:
			return Map.TilePtr(row+1,col+1);
		default:
			return NULL;
	}
}
int Actor::DirectionOfOnly(string tilename,bool ignore_actors,bool orth){
	int total=0;
	int dir=0;
	if(!orth && Map.TilePtr(row-1,col-1)->Name()==tilename){
		if(ignore_actors || Map.ActorPtr(row-1,col-1)==NULL){
			++total;
			dir=7;
		}
	}
	if(Map.TilePtr(row-1,col)->Name()==tilename){
		if(ignore_actors || Map.ActorPtr(row-1,col)==NULL){
			++total;
			dir=8;
		}
	}
	if(!orth && Map.TilePtr(row-1,col+1)->Name()==tilename){
		if(ignore_actors || Map.ActorPtr(row-1,col+1)==NULL){
			++total;
			dir=9;
		}
	}
	if(Map.TilePtr(row,col-1)->Name()==tilename){
		if(ignore_actors || Map.ActorPtr(row,col-1)==NULL){
			++total;
			dir=4;
		}
	}
	if(Map.TilePtr(row,col+1)->Name()==tilename){
		if(ignore_actors || Map.ActorPtr(row,col+1)==NULL){
			++total;
			dir=6;
		}
	}
	if(!orth && Map.TilePtr(row+1,col-1)->Name()==tilename){
		if(ignore_actors || Map.ActorPtr(row+1,col-1)==NULL){
			++total;
			dir=1;
		}
	}
	if(Map.TilePtr(row+1,col)->Name()==tilename){
		if(ignore_actors || Map.ActorPtr(row+1,col)==NULL){
			++total;
			dir=2;
		}
	}
	if(!orth && Map.TilePtr(row+1,col+1)->Name()==tilename){
		if(ignore_actors || Map.ActorPtr(row+1,col+1)==NULL){
			++total;
			dir=3;
		}
	}
	if(total > 1){
		return -1;
	}
	else{
		if(total==1){
			return dir;
		}
		else{
			return 0;
		}
	}
}
int Actor::ActorsAround(){
	int total=-1;
	for(int i=row-1;i<=row+1;++i){ //no bounds check
		for(int j=col-1;j<=col+1;++j){
			if(Map.ActorPtr(i,j)!=NULL){
				++total;
			}
		}
	}
	return total;
}
void Actor::RefreshLevel(){ //clears things that should no longer be remembered
	player_seen=NULL;
	target=NULL;
}
void Actor::Q0(){
	Q.AddToFront(new Event(this,0,MOVE));
}
void Actor::Q1(){
	Q.Add(new Event(this,100,MOVE));
}
void Actor::QS(){
	Q.Add(new Event(this,speed,MOVE));
}
void Actor::RemoveMinion(Actor* ptr){
	int i;
	for(i=0;i<minions.size();++i){
		if(minions[i] == ptr){
			break;
		}
	}
	if(i != minions.size()){
		minions.erase(minions.begin()+i,minions.begin()+i+1);
	}
}
bool Actor::PayMP(int num){
	if(curmp >= num){
		curmp -= num;
		return true;
	}
	else{
		return false;
	}
}
void Actor::Summon(int r,int c){
	Move(r,c);
	QS();
}
void Actor::Unsummon(){
	Q.KillEvents(this,ANY_EVENT);
	Map.RemoveTargets(this);
	Map.DelActorPtr(row,col);
	RefreshLevel();
	row=-1;
	col=-1;
}
string Actor::SpellName(SpellType spell){
	string result[NUM_SPELLS] = {"Raise skeleton","Raise zombie","Raise ghoul","Raise mummy","Raise wight","Raise shade","Raise spirit","Raise wraith","Raise banshee","Raise spectre","Raise draugr","Grim feast","Detect living","Detect undead","Hide from undead","Ghoul's touch","Bind undead","Steal item","Unsummon"};
	return result[int(spell)];
}
int Actor::SpellCost(SpellType spell){
	int result[NUM_SPELLS] = {
		3,3,3,4,4,
		4,4,5,5,5,
		5,1,1,1,4,
		2,5,4,2};
	return result[int(spell)];
}
void Actor::CastSpell(SpellType spell,Tile* ptr){
	bool cast=true;
	switch(spell){
		case R_SKELETON:
		case R_ZOMBIE:
		case R_GHOUL:
		case R_MUMMY:
		case R_WIGHT:
		case R_SHADE:
		case R_SPIRIT:
		case R_WRAITH:
		case R_BANSHEE:
		case R_SPECTRE:
		case R_DRAUGR:
			{
				if(minions.size() < 4){
					vector<Tile*> empty_tiles;
					for(int i=row-1;i<=row+1;++i){
						for(int j=col-1;j<=col+1;++j){
							if(Map.ActorPtr(i,j)==NULL && (Map.TilePtr(i,j)->IsWalkable() || LoadedActor[int(spell)+2].HasAttr(INCORPOREAL))){
								if(i!=row || j!=col){ //excluding your position
									empty_tiles.insert(empty_tiles.end(),Map.TilePtr(i,j));
								}
							}
						}
					}
					if(empty_tiles.size() > 0){
						Item* temp = SelectItem("Choose a corpse to raise: ");
						if(temp!=NULL && temp->ID()==29){ //a corpse, of course
							if(temp->Percent() == 100){
								LoseItem(temp);
								Tile* tileptr = empty_tiles[rand()%empty_tiles.size()];
								Actor* actorptr = new Actor(LoadedActor[int(spell)+2],-1,-1);
								actorptr->MakeAllied(true);
								AddMinion(actorptr);
								actorptr->curhp = actorptr->maxhp/2; //raised with 50% health
								actorptr->Summon(tileptr->Row(),tileptr->Col());
								Buff.Rec("You raise a new minion. ");
							}
							else{
								Buff.Rec("You need a whole corpse to raise. ");
								cast=false;
							}
						}
						else{
							cast=false;
						}
					}
					else{
						Buff.Rec("There's no room! ");
						cast=false;
					}
				}
				else{
					Buff.Rec("You already have four minions. ");
					cast=false;
				}
				break;
			}
		case GRIM_FEAST:
			{
				if(curhp < maxhp){
					Item* temp = SelectItem("Choose a corpse to consume: ");
					if(temp!=NULL && temp->ID()==29){
						if(temp->Percent() >= (maxhp - curhp)){
							temp->ReducePercent(maxhp - curhp);
							curhp = maxhp;
							if(temp->Percent() <= 0){
								LoseItem(temp);
							}
						}
						else{
							curhp += temp->Percent();
							LoseItem(temp);
						}
						Buff.Rec("Delicious. ");
					}
					else{
						cast=false;
					}
				}
				else{
					Buff.Rec("You're already at full health. ");
					cast=false;
				}
				break;
			}
		case DETECT_LIVING:
			{
				prw(mapwin);
				bool det=false;
				for(int i=0;i<ROWS;++i){
					for(int j=0;j<COLS;++j){
						if(DistanceFrom(i,j)<=8 && Map.ActorPtr(i,j)!=NULL && !Map.ActorPtr(i,j)->HasAttr(UNDEAD) && Map.ActorPtr(i,j)!=this){
							Map.ActorPtr(i,j)->Draw();
							det=true;
						}
						else{
							mpr(i,j," ");
						}
					}
				}
				if(det){
					top_panel(msgpan);
					update_panels();
					doupdate();
					Buff.Now("You detect the living. (press any key)");
					getch();
				}
				else{
					Buff.Rec("You detect no living beings nearby. ");
				}
				break;
			}
		case DETECT_UNDEAD:
			{
				prw(mapwin);
				bool det=false;
				for(int i=0;i<ROWS;++i){
					for(int j=0;j<COLS;++j){
						if(DistanceFrom(i,j)<=8 && Map.ActorPtr(i,j)!=NULL && Map.ActorPtr(i,j)->HasAttr(UNDEAD) && Map.ActorPtr(i,j)!=this){
							Map.ActorPtr(i,j)->Draw();
							det=true;
						}
						else{
							mpr(i,j," ");
						}
					}
				}
				if(det){
					top_panel(msgpan);
					update_panels();
					doupdate();
					Buff.Now("You detect the undead. (press any key)");
					getch();
				}
				else{
					Buff.Rec("You detect no undead beings nearby. ");
				}
				break;
			}
		case HIDE_FROM_UNDEAD:
			{
				Buff.Rec("You feel hidden. ");
				GainAttr(HIDDEN_FROM_UNDEAD);
				Q.Add(new Event(this,2100,REMOVE_ATTR,HIDDEN_FROM_UNDEAD,"You feel less hidden. "));
				break;
			}
/*		case GHOULS_TOUCH:
			{
				Buff.Rec("You grow claws. ");
				GainAttr(PARALYSIS_HIT);
				Q.Add(new Event(this,2100,REMOVE_ATTR,PARALYSIS_HIT,"Your claws retract. "));
				break;
			}
//		case BIND_UNDEAD:
//			{
				//later
//				break;
//			}
		case STEAL_ITEM:
			{
				Buff.Now("Steal an item in what direction? ");
				int dir = GetDirection();
				if(dir!=0){
					//later
				}
				else{
					cast=false;
				}
				break;
			}
		case UNSUMMON:
			{
				//later - just requires some targeting
				break;
			}*/
		default: break;
	}
	if(cast){
		Q1();
	}
	else{
		Q0();
	}
}

